/*
 * Tezad - Classic puzzle game
 * Copyright 2009 Shayne Riley and Paul Maseberg
 *
 * Tezad is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tezad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tezad.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <pezads at gmail dot com>
 */


#ifndef FUNCTOR_H_
#define FUNCTOR_H_

#include <cstring> // Needed for memcpy

/**
 * This is implemented based on research in the paper, "Callbacks in C++ using
 * Template Functors" written by Rich Hickey in 1994. This paper might be
 * available at http://www.tutok.sk/fastgl/callback.html
 *
 * Although FunctorBase *could* be used directly, is is much easier to call
 * makeFunctor([and so on]) instead.
 */
class FunctorBase
{
public:
    typedef void (FunctorBase::*MemberFunction_t)();
    typedef void (*Function_t)();

    FunctorBase() : function(0), callee(0) { /* Empty */ }

    FunctorBase(const void *clazz, const void *callback, size_t size)
    {
        // if clazz isn't zero, then the callback must be a member function.
        if(clazz)
        {
            callee = (const void *) clazz;
            memcpy(memberFunction, callback, size);
        }
        else
        {
            // Otherwise, the callback is a plain old function.
            function = callback;
        }
    }

    // for evaluation in conditions
    operator bool() const { return function || callee; }

    class DummyInit
    {
    };

    union
    {
        const void *function;
        char memberFunction[sizeof(MemberFunction_t)];
    };
    const void *callee;
};

class Translator
{
public:
};

//class Functor0
//{
//public:
//    void operator()() const;
//    //...
//};

/**
 * The one-parameter functor which can be used for either a function callback
 * or a member function callback. makeFunctor will call either
 * FunctionTranslator or MemberTranslator depending on which one is needed.
 */
template <class P1>
class Functor1 : protected FunctorBase
{
public:
    Functor1(DummyInit * = 0) { /* Empty */ }

    void operator()(P1 param1) const
    {
        thunk(*this, param1);
    }

    FunctorBase::operator bool;

protected:
    typedef void (*Thunk)(const FunctorBase &, P1);

    Functor1(Thunk t, const void *clazz, const void *inFunction, size_t size)
    : FunctorBase(clazz, inFunction, size), thunk(t)
    {
        /* Empty */
    }

private:
    Thunk thunk;
};

/**
 * A functor for a one-parameter function callback.
 */
template <class P1, class Func>
class FunctionTranslator1 : public Functor1<P1>
{
public:
    FunctionTranslator1(Func function)
    : Functor1<P1>(thunk, 0, function, 0)
    {
        /* Empty */
    }

    static void thunk(const FunctorBase &functor, P1 param1)
    {
        (Func(functor.function))(param1);
    }
};

/**
 * A functor for a one-parameter member function callback.
 */
template <class P1, class Callee, class MemFunc>
class MemberTranslator1 : public Functor1<P1>
{
public:
    MemberTranslator1(Callee &c, const MemFunc &m)
    : Functor1<P1>(thunk, &c, &m, sizeof(MemFunc))
    {
        /* Empty */
    }

    static void thunk(const FunctorBase &functor, P1 param1)
    {
        Callee *callee = (Callee *)functor.callee;
        MemFunc &memberFunction(*(MemFunc*)(void *)(functor.memberFunction));
        (callee->*memberFunction)(param1);
    }
};

/**
 * Creates a functor with a pointer-to-function of one parameter.
 * @param dummy argument used to tell the type of the functor. Will be removed
 *  once I figure out what in the world is going on.
 * @param function  The function to call.
 * @return a callback functor.
 */
template <class P1, class TRT, class TP1>
inline FunctionTranslator1<P1, TRT (*)(TP1)> makeFunctor(
    Functor1<P1>*, TRT (*function)(TP1))
{
    return FunctionTranslator1<P1, TRT (*)(TP1)>(function);
}

/**
 * Creates a functor with a non-const-member-function of one parameter.
 * @param dummy argument to tell the type of the functor. To be removed.
 * @param clazz The class whose function is to be called.
 * @param function This function is to be called.
 * @return a callback functor.
 */
template <class P1, class Callee, class TRT, class CallType, class TP1>
inline MemberTranslator1<P1, Callee, TRT (CallType::*)(TP1)> makeFunctor(
    Functor1<P1>*, Callee &clazz, TRT (CallType::* const &function)(TP1))
{
    typedef TRT (CallType::*MemFunc)(TP1);
    return MemberTranslator1<P1, Callee, MemFunc>(clazz, function);
}

/**
 * Creates a functor with a const-member-function of one parameter.
 * @param dummy argument to tell the type of the functor. To be removed.
 * @param clazz The class whose function is to be called.
 * @param function This function is to be called.
 * @return a callback functor.
 */
template <class P1, class Callee, class TRT, class CallType, class TP1>
inline MemberTranslator1<P1, const Callee, TRT (CallType::*)(TP1) const>
makeFunctor(Functor1<P1>*, const Callee &clazz,
    TRT (CallType::* const &function)(TP1) const)
{
    typedef TRT (CallType::*MemFunc)(TP1) const;
    return MemberTranslator1<P1, const Callee, MemFunc>(clazz, function);
}



//template <class RT>
//class Functor0wRet
//{
//public:
//    RT operator()() const;
//    //...
//};
//
//template <class P1, class RT>
//class Functor1wRet
//{
//public:
//    RT operator()(P1 p1) const;
//    //...
//};
//

#endif /* FUNCTOR_H_ */
